﻿using System;
using System.Windows.Forms;

using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;

using AnimatingHair.Entity;
using AnimatingHair.Rendering;
using AnimatingHair.Initializing;
using AnimatingHair.Rendering.Debug;

namespace AnimatingHair
{
    class SimpleWindow : GameWindow
    {
        #region Members

        private Scene scene;
        private SceneInitializer sceneInitializer;
        private IRenderable renderer;
        private Camera camera;
        private Vector3d offset;
        double distance = 10, elevation = 0, azimuth = 0, mouseX, mouseY;

        //debug:
        private VoxelGridRenderer voxelGridRenderer;

        #endregion

        protected override void OnLoad( EventArgs e )
        {
            checkOpenGLVersion();

            sceneInitializer = new SceneInitializer();
            scene = sceneInitializer.InitializeScene();
            camera = new Camera();
            //renderer = new Renderer( camera, scene );
            renderer = new SimpleRenderer( camera, scene );

            offset = scene.Hair.CenterPosition();

            //debug:
            voxelGridRenderer = new VoxelGridRenderer( scene.VoxelGrid );

            Keyboard.KeyDown += keyDownEvent;
        }

        private void keyDownEvent( object sender, KeyboardKeyEventArgs e )
        {
            // DEBUG
            if ( Keyboard[ Key.Space ] )
            {
                scene.Step();
                Const.Time++;
            }
        }

        protected override void OnUpdateFrame( FrameEventArgs e )
        {
            if ( Keyboard[ Key.Escape ] )
                Exit();

            //debug:
            sphereMovementControl();

            cameraMovementControl();
            polarToCartesian();
            camera.Eye += offset;
            camera.Target += offset;
        }

        private void sphereMovementControl()
        {
            if ( Keyboard[ Key.Left ] )
            {
            }

            if ( Keyboard[ Key.Right ] )
            {
            }

            if ( Keyboard[ Key.Up ] )
            {
            }

            if ( Keyboard[ Key.Down ] )
            {
            }

            if ( Keyboard[ Key.PageUp ] )
            {
            }

            if ( Keyboard[ Key.PageDown ] )
            {
            }
        }

        protected override void OnRenderFrame( FrameEventArgs e )
        {
            renderer.Render();

            //debug:
            //voxelGridRenderer.Render();


            // misc rendering here (eg. FPS) :
            Console.Clear();
            Console.WriteLine( "FPS: " + ((int)(1 / e.Time) + 1).ToString( "F0" ) );
            Console.WriteLine( Const.Time );

            SwapBuffers();

            scene.Step();
            Const.Time++;
        }

        private void cameraMovementControl()
        {
            if ( Mouse[ MouseButton.Left ] || Mouse[ MouseButton.Right ] )
            {
                azimuth += 0.1 * (Mouse.X - mouseX);
                if ( azimuth > 360 )
                    azimuth -= 360;

                elevation -= 0.1 * (Mouse.Y - mouseY);
                if ( elevation > 89.99 )
                    elevation = 89.99;
                if ( elevation < -89.99 )
                    elevation = -89.99;
            }

            mouseX = Mouse.X;
            mouseY = Mouse.Y;

            if ( Keyboard[ Key.W ] )
            {
                distance -= 0.1;
            }

            if ( Keyboard[ Key.S ] )
            {
                distance += 0.1;
            }
        }

        private void polarToCartesian()
        {
            double x, y, z, k;
            double ele2 = -elevation * Math.PI / 180;
            double azi2 = (azimuth + 90) * Math.PI / 180;

            k = Math.Cos( ele2 );
            z = distance * Math.Sin( ele2 );
            y = distance * Math.Sin( azi2 ) * k;
            x = distance * Math.Cos( azi2 ) * k;

            camera.Eye = new Vector3d( x, z, y );
            camera.Target = new Vector3d( 0, 0, 0 );
            camera.Up = Vector3d.UnitY;
        }

        private void checkOpenGLVersion()
        {
            string version = GL.GetString( StringName.Version );
            int major = version[ 0 ];
            if ( major < 2 )
            {
                MessageBox.Show( "You need at least OpenGL 2.0 to run this example. Aborting.", "GLSL not supported",
                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation );
                Exit();
            }
        }

        protected override void OnResize( EventArgs e )
        {
            GL.Viewport( ClientRectangle );

            float aspect_ratio = Width / (float)Height;
            Matrix4 perpective = Matrix4.CreatePerspectiveFieldOfView( MathHelper.PiOver4, aspect_ratio, 1, 64 );
            GL.MatrixMode( MatrixMode.Projection );
            GL.LoadMatrix( ref perpective );
        }

        /// <summary>Creates a 800x600 window with the specified title.</summary>
        public SimpleWindow()
            : base( 800, 600, GraphicsMode.Default, "Animating Hair" )
        {
            VSync = VSyncMode.On;
        }
    }
}
